Kuasai berkas deklarasi TypeScript (.d.ts) untuk membuka keamanan tipe dan pelengkapan otomatis untuk pustaka JavaScript apa pun. Pelajari cara menggunakan @types, membuat definisi sendiri, dan menangani kode pihak ketiga seperti seorang profesional.
Membuka Ekosistem JavaScript: Pendalaman Berkas Deklarasi TypeScript
TypeScript telah merevolusi pengembangan web modern dengan membawa pengetikan statis ke dunia dinamis JavaScript. Keamanan tipe ini memberikan manfaat luar biasa: menangkap kesalahan pada waktu kompilasi, mengaktifkan pelengkapan otomatis editor yang kuat, dan membuat basis kode besar jauh lebih mudah dipelihara. Namun, tantangan utama muncul ketika kita ingin menggunakan ekosistem pustaka JavaScript yang luas—yang sebagian besar tidak ditulis dalam TypeScript. Bagaimana kode TypeScript kita yang diketik secara ketat memahami bentuk, fungsi, dan variabel dari pustaka JavaScript yang tidak diketik?
Jawabannya terletak pada Berkas Deklarasi TypeScript. Berkas-berkas ini, yang dapat diidentifikasi dengan ekstensi .d.ts, adalah jembatan penting antara dunia TypeScript dan JavaScript. Mereka bertindak sebagai cetak biru atau kontrak API, yang menjelaskan tipe dari pustaka pihak ketiga tanpa berisi implementasi sebenarnya. Dalam panduan komprehensif ini, kita akan menjelajahi semua yang perlu Anda ketahui untuk mengelola definisi tipe dengan percaya diri untuk pustaka JavaScript apa pun dalam proyek TypeScript Anda.
Apa Sebenarnya Berkas Deklarasi TypeScript Itu?
Bayangkan Anda telah mempekerjakan seorang kontraktor yang hanya berbicara bahasa yang berbeda. Untuk bekerja dengan mereka secara efektif, Anda memerlukan penerjemah atau serangkaian instruksi terperinci dalam bahasa yang Anda berdua pahami. Berkas deklarasi melayani tujuan yang sama persis untuk kompilator TypeScript (kontraktor).
Berkas .d.ts hanya berisi informasi tipe. Ini termasuk:
- Tanda tangan untuk fungsi dan metode (tipe parameter, tipe kembalian).
- Definisi untuk variabel dan tipenya.
- Antarmuka dan alias tipe untuk objek kompleks.
- Definisi kelas, termasuk properti dan metodenya.
- Struktur namespace dan modul.
Yang terpenting, berkas-berkas ini tidak berisi kode yang dapat dieksekusi. Mereka murni untuk analisis statis. Ketika Anda mengimpor pustaka JavaScript seperti Lodash ke dalam proyek TypeScript Anda, kompilator mencari berkas deklarasi yang sesuai. Jika menemukannya, ia dapat memvalidasi kode Anda, memberikan pelengkapan otomatis yang cerdas, dan memastikan Anda menggunakan pustaka dengan benar. Jika tidak, ia akan memunculkan kesalahan seperti: Could not find a declaration file for module 'lodash'.
Mengapa Berkas Deklarasi Tidak Dapat Dinegosiasikan untuk Pengembangan Profesional
Menggunakan pustaka JavaScript tanpa definisi tipe yang tepat dalam proyek TypeScript merusak alasan utama penggunaan TypeScript sejak awal. Mari kita pertimbangkan skenario sederhana menggunakan pustaka utilitas populer, Lodash.Dunia Tanpa Definisi Tipe
Tanpa berkas deklarasi, TypeScript tidak tahu apa itu lodash atau apa isinya. Untuk membuat kode dikompilasi, Anda mungkin tergoda untuk menggunakan perbaikan cepat seperti ini:
const _: any = require('lodash');
const users = [{ 'user': 'barney' }, { 'user': 'fred' }];
// Autocomplete? No help here.
// Type checking? No. Is 'username' the correct property?
// The compiler allows this, but it might fail at runtime.
_.find(users, { username: 'fred' });
Dalam kasus ini, variabel _ bertipe any. Ini secara efektif memberi tahu TypeScript, "Jangan periksa apa pun yang terkait dengan variabel ini." Anda kehilangan semua manfaat: tidak ada pelengkapan otomatis, tidak ada pemeriksaan tipe pada argumen, dan tidak ada kepastian tentang tipe kembalian. Ini adalah tempat berkembang biaknya kesalahan runtime.
Dunia Dengan Definisi Tipe
Sekarang, mari kita lihat apa yang terjadi ketika kita menyediakan berkas deklarasi yang diperlukan. Setelah menginstal tipe (yang akan kita bahas selanjutnya), pengalaman diubah:
import _ from 'lodash';
interface User {
user: string;
active?: boolean;
}
const users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];
// 1. Editor provides autocompletion for 'find' and other lodash functions.
// 2. Hovering over 'find' shows its full signature and documentation.
// 3. TypeScript sees that `users` is an array of `User` objects.
// 4. TypeScript knows the predicate for `find` on `User[]` should involve `user` or `active`.
// CORRECT: TypeScript is happy.
const fred = _.find(users, { user: 'fred' });
// ERROR: TypeScript catches the mistake!
// Property 'username' does not exist on type 'User'.
const betty = _.find(users, { username: 'betty' });
Perbedaannya sangat mencolok. Kita mendapatkan keamanan tipe penuh, pengalaman pengembang yang unggul melalui peralatan, dan pengurangan dramatis dalam potensi bug. Ini adalah standar profesional untuk bekerja dengan TypeScript.
Hierarki Menemukan Definisi Tipe
Jadi, bagaimana Anda mendapatkan berkas .d.ts ajaib ini untuk pustaka favorit Anda? Ada proses yang mapan yang mencakup sebagian besar skenario.
Langkah 1: Periksa Apakah Pustaka Mengemas Tipe Sendiri
Skenario terbaik adalah ketika pustaka ditulis dalam TypeScript atau pengelolanya menyediakan berkas deklarasi resmi dalam paket yang sama. Ini menjadi semakin umum untuk proyek modern yang dikelola dengan baik.
Cara memeriksa:
- Instal pustaka seperti biasa:
npm install axios - Lihat di dalam folder pustaka di
node_modules/axios. Apakah Anda melihat berkas.d.ts? - Periksa berkas
package.jsonpustaka untuk bidang"types"atau"typings". Bidang ini mengarah langsung ke berkas deklarasi utama. Misalnya,package.jsonAxios berisi:"types": "index.d.ts".
Jika kondisi ini terpenuhi, Anda selesai! TypeScript akan secara otomatis menemukan dan menggunakan tipe yang dikemas ini. Tidak diperlukan tindakan lebih lanjut.
Langkah 2: Proyek DefinitelyTyped (@types)
Untuk ribuan pustaka JavaScript yang tidak mengemas tipe mereka sendiri, komunitas TypeScript global telah menciptakan sumber daya yang luar biasa: DefinitelyTyped.
DefinitelyTyped adalah repositori yang dikelola komunitas terpusat di GitHub yang menyimpan berkas deklarasi berkualitas tinggi untuk sejumlah besar paket JavaScript. Definisi ini diterbitkan ke registri npm di bawah cakupan @types.
Cara menggunakannya:
Jika pustaka seperti lodash tidak mengemas tipenya sendiri, Anda cukup menginstal paket @types yang sesuai sebagai dependensi pengembangan:
npm install --save-dev @types/lodash
Konvensi penamaannya sederhana dan dapat diprediksi: untuk paket bernama package-name, tipenya hampir selalu berada di @types/package-name. Anda dapat mencari tipe yang tersedia di situs web npm atau langsung di repositori DefinitelyTyped.
Mengapa --save-dev? Berkas deklarasi hanya diperlukan selama pengembangan dan kompilasi. Mereka tidak berisi kode runtime apa pun, jadi mereka tidak boleh dimasukkan dalam bundel produksi akhir Anda. Menginstalnya sebagai devDependency memastikan pemisahan ini.
Langkah 3: Ketika Tidak Ada Tipe - Menulis Sendiri
Bagaimana jika Anda menggunakan pustaka pribadi internal yang lebih lama, khusus, atau tidak mengemas tipe dan tidak ada di DefinitelyTyped? Dalam hal ini, Anda perlu menyingsingkan lengan baju dan membuat berkas deklarasi Anda sendiri. Meskipun ini mungkin terdengar menakutkan, Anda dapat mulai sederhana dan menambahkan lebih banyak detail sesuai kebutuhan.
Perbaikan Cepat: Deklarasi Modul Ambient Singkat
Terkadang, Anda hanya perlu membuat proyek Anda dikompilasi tanpa kesalahan saat Anda mencari tahu strategi pengetikan yang tepat. Anda dapat membuat berkas di proyek Anda (mis., declarations.d.ts atau types/global.d.ts) dan menambahkan deklarasi singkat:
// in a .d.ts file
declare module 'some-untyped-library';
Ini memberi tahu TypeScript, "Percayalah, modul bernama 'some-untyped-library' ada. Perlakukan saja semua yang diimpor darinya sebagai tipe any." Ini membungkam kesalahan kompilator, tetapi seperti yang telah kita bahas, ia mengorbankan semua keamanan tipe untuk pustaka itu. Ini adalah tambalan sementara, bukan solusi jangka panjang.
Membuat Berkas Deklarasi Kustom Dasar
Pendekatan yang lebih baik adalah mulai mendefinisikan tipe untuk bagian pustaka yang benar-benar Anda gunakan. Katakanlah kita memiliki pustaka sederhana bernama `string-utils` yang mengekspor satu fungsi.
// In node_modules/string-utils/index.js
module.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
Kita dapat membuat berkas string-utils.d.ts di direktori `types` khusus di root proyek kita.
// In my-project/types/string-utils.d.ts
declare module 'string-utils' {
export function capitalize(str: string): string;
// You could add other function definitions here as you use them
// export function slugify(str: string): string;
}
Sekarang, kita perlu memberi tahu TypeScript di mana menemukan definisi tipe kustom kita. Kita melakukan ini di tsconfig.json:
{
"compilerOptions": {
// ... other options
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
}
}
Dengan pengaturan ini, ketika Anda import { capitalize } from 'string-utils', TypeScript akan menemukan berkas deklarasi kustom Anda dan memberikan keamanan tipe yang Anda tentukan. Anda dapat secara bertahap membuat berkas ini saat Anda menggunakan lebih banyak fitur pustaka.
Menyelami Lebih Dalam: Menulis Berkas Deklarasi
Mari kita jelajahi beberapa konsep yang lebih canggih yang akan Anda temui saat menulis atau membaca berkas deklarasi.
Mendeklarasikan Berbagai Jenis Ekspor
Modul JavaScript dapat mengekspor berbagai cara. Berkas deklarasi Anda harus sesuai dengan struktur ekspor pustaka.
- Ekspor Bernama: Ini adalah yang paling umum. Kita melihatnya di atas dengan `export function capitalize(...)`. Anda juga dapat mengekspor konstanta, antarmuka, dan kelas.
- Ekspor Default: Untuk pustaka yang menggunakan `export default`.
- UMD Globals: Untuk pustaka yang lebih lama yang dirancang untuk bekerja di browser melalui tag
<script>, mereka sering melampirkan diri ke objek `window` global. Anda dapat mendeklarasikan variabel global ini. - `export =` and `import = require()`: Sintaks ini untuk modul CommonJS yang lebih lama yang menggunakan `module.exports = ...`. Misalnya, jika pustaka melakukan `module.exports = myClass;`.
declare module 'my-lib' {
export const version: string;
export interface Options { retries: number; }
export function doSomething(options: Options): Promise
declare module 'my-default-lib' {
// For a function default export
export default function myCoolFunction(): void;
// For an object default export
// const myLib = { name: 'lib', version: '1.0' };
// export default myLib;
}
// Declares a global variable '$' of a certain type
declare var $: JQueryStatic;
// in my-class.d.ts
declare class MyClass { constructor(name: string); }
export = MyClass;
// in your app.ts
import MyClass = require('my-class');
const instance = new MyClass('test');
Meskipun kurang umum dengan Modul ES modern, ini sangat penting untuk kompatibilitas dengan banyak paket Node.js yang lebih lama tetapi masih banyak digunakan.
Augmentasi Modul: Memperluas Tipe yang Ada
Salah satu fitur yang paling kuat adalah augmentasi modul (juga dikenal sebagai penggabungan deklarasi). Ini memungkinkan Anda untuk menambahkan properti ke antarmuka yang ada yang didefinisikan dalam berkas deklarasi paket lain. Ini sangat berguna untuk pustaka dengan arsitektur plugin, seperti Express atau Fastify.
Bayangkan Anda menggunakan middleware di Express yang menambahkan properti `user` ke objek `Request`. Tanpa augmentasi, TypeScript akan mengeluh bahwa `user` tidak ada di `Request`.
Berikut cara Anda dapat memberi tahu TypeScript tentang properti baru ini:
// in your types/express.d.ts file
// We must import the original type to augment it
import { UserProfile } from './auth'; // Assuming you have a UserProfile type
// Tell TypeScript we're augmenting the 'express-serve-static-core' module
declare module 'express-serve-static-core' {
// Target the 'Request' interface inside that module
interface Request {
// Add our custom property
user?: UserProfile;
}
}
Sekarang, di seluruh aplikasi Anda, objek `Request` Express akan diketik dengan benar dengan properti `user` opsional, dan Anda akan mendapatkan keamanan tipe penuh dan pelengkapan otomatis.
Arahan Tiga Garis Miring
Anda mungkin terkadang melihat komentar di bagian atas berkas .d.ts yang dimulai dengan tiga garis miring (///). Ini adalah arahan tiga garis miring, yang bertindak sebagai instruksi kompilator.
/// <reference types="..." />: Ini adalah yang paling umum. Secara eksplisit menyertakan definisi tipe paket lain sebagai dependensi. Misalnya, tipe untuk plugin WebdriverIO mungkin menyertakan/// <reference types="webdriverio" />karena tipenya sendiri bergantung pada tipe WebdriverIO inti./// <reference path="..." />: Ini digunakan untuk mendeklarasikan dependensi pada berkas lain dalam proyek yang sama. Ini adalah sintaks yang lebih lama, sebagian besar digantikan oleh impor modul ES.
Praktik Terbaik untuk Mengelola Berkas Deklarasi
- Pilih Tipe yang Dikemas: Saat memilih antara pustaka, pilih yang ditulis dalam TypeScript atau mengemas definisi tipe resmi mereka sendiri. Ini menandakan komitmen terhadap ekosistem TypeScript.
- Simpan
@typesdidevDependencies: Selalu instal paket@typesdengan--save-devatau-D. Mereka tidak diperlukan untuk kode produksi Anda. - Sejajarkan Versi: Sumber kesalahan umum adalah ketidakcocokan antara versi pustaka dan versi
@types-nya. Kenaikan versi utama dalam pustaka (mis., dari v2 ke v3) kemungkinan akan memiliki perubahan yang merusak di API-nya, yang harus tercermin dalam paket@types. Cobalah untuk menyinkronkannya. - Gunakan
tsconfig.jsonuntuk Kontrol: Opsi kompilatortypeRootsdantypesditsconfig.jsonAnda dapat memberi Anda kontrol yang baik atas tempat TypeScript mencari berkas deklarasi.typeRootsmemberi tahu kompilator folder mana yang harus diperiksa (secara default, itu./node_modules/@types), dantypesmemungkinkan Anda untuk secara eksplisit mencantumkan paket tipe mana yang akan disertakan. - Berkontribusi Kembali: Jika Anda menulis berkas deklarasi komprehensif untuk pustaka yang tidak memilikinya, pertimbangkan untuk menyumbangkannya ke proyek DefinitelyTyped. Ini adalah cara yang fantastis untuk memberikan kembali kepada komunitas pengembang global dan membantu ribuan orang lain.
Kesimpulan: Pahlawan Tanpa Tanda Jasa dari Keamanan Tipe
Berkas Deklarasi TypeScript adalah pahlawan tanpa tanda jasa yang memungkinkan untuk mengintegrasikan dunia JavaScript yang dinamis dan luas dengan mulus ke dalam lingkungan pengembangan yang kuat dan aman untuk tipe. Mereka adalah penghubung penting yang memberdayakan alat kita, mencegah kesalahan yang tak terhitung jumlahnya, dan membuat basis kode kita lebih tangguh dan mendokumentasikan diri sendiri.
Dengan memahami cara menemukan, menggunakan, dan bahkan membuat berkas .d.ts Anda sendiri, Anda tidak hanya memperbaiki kesalahan kompilator—Anda meningkatkan seluruh alur kerja pengembangan Anda. Anda membuka potensi penuh dari TypeScript dan ekosistem pustaka JavaScript yang kaya, menciptakan sinergi yang kuat yang menghasilkan perangkat lunak yang lebih baik dan lebih andal untuk audiens global.